How to create instances of CFCs in a CommandBox Task Runner

Someone asked me today why they couldn’t run the following code inside of a Task Runner without getting an error that the CFC couldn’t be found:

new lib.providers.s3();

They wanted to know how to create the CFC. I figured the answer(s) were worth sharing since there’s several ways to skin this cat.

Why it doesn’t work

This is because Lucee’s “web root” is not the current working directory that your task is running out of. While Task Runners themselves are “aware” of their surroundings, the native CFML code isn’t. If you were to dump out expandPath( '/' ) from your task, you’d see the folder where Lucee is looking for /lib/providers.s3.cfc in and it’s probably not at all related to your shell’s current working directory at all. You shouldn’t ever depend on what Lucee thinks the ‘web root’ is since it’s not reliable.

Here’s 4 quick ways to create arbitrary CFCs from inside your Task Runner.

Load the CFCs inside a Module

This is the ideal method as it’s very easy and self-contained. Define a module, whether it’s stored on ForgeBox and installed as a dependency or just something you’ve committed in your code that lives next to the task.cfc doesn’t matter.

- task.cfc
- modules_app/
  - s3Libs/
    - ModuleConfig.cfc <- Only needs an empty configure() method
    - models/
      - providers/
        - S3.cfc

Then, you’d just load the module in your task like this

loadModule( 'modules_app/s3Libs' );

and then just use it as

getInstance( 'S3@s3Libs' );

Remember, a loaded module remains in memory even after the task has finished executing until the CLI is closed.

Map a folder of CFCs on-the-fly

WireBox can be asked to scan any folder of CFCs for you and map them. Mapping a dir of CFCs manually would look like this:

wirebox
  .getBinder()
  .mapDirectory(
    packagePath=resolvePath( 'lib/providers' ),
    namespace="taskProvidersWhatever"
  );

The resolvepath() will give you the full absolute path to the folder, and the namespace can be whatever you like. You don’t have to use a namespace, but you want to in order to prevent any of your CFCs overriding core WireBox mappings which could break CommandBox.
then just ask for it

getinstance( 's3@taskProvidersWhatever' )

This is really more/less what happens when a module is loaded except the namespace is just the module name. Remember, mapped CFC’s in WireBox remain in memory even after the task has finished executing until the CLI is closed.

Add a quick and dirty CF Mapping

If you don’t want to use WireBox at all, you can create a CF mapping that points to a folder of your choice, and then use that mapping as the “root” of your CFC path

fileSystemUtil.createMapping( '/taskRoot', resolvepath( '' ) );

That would create a CF mapping in the CLI called /taskRoot that pointed to the folder the task.cfc lived in.
Then you can do

new taskRoot.lib.providers.s3()

Remember, CF Mappings created in the CLI remain in memory even after the task has finished executing until the CLI is closed.

Force the CFC path to be relative

This approach still adds a CF mapping behind the scenes. Since a CFC path MUST be relative, CommandBox has a helper that will take an absolute path and force it to be relative, creating new CF mappings where neccessary.

absoluteCFCPath = resolvePath( 'lib/providers/s3' );
relativeCFCPath = fileSystemUtil.makePathRelative( absoluteCFCPath );
// This blind replacement won't work for a Windows UNC network drive, BTW
CFCDotPath = relativeCFCPath.listChangeDelims( '.', '/\' );
createOBject( "component", CFCDotPath ).init()

basically that turns

lib/providers/s3

into

C:/full/path/to/lib/providers/s3

and then turns that into a relative path

/C_DRIVE/full/path/to/lib/providers/s3

(which creates a CF mapping on the fly, if needed)
and then turns that to

C_DRIVE.full.path.to.lib.providers.s3

which becomes your CFC path

1 Like

You can’t imaging how perfect the time timing of this post is Brad! We were having this very problem yesterday before I left for the day and this morning before I even got to troubleshoot it, you posted the answer.

Thanks for always sharing great information!!!

1 Like